home *** CD-ROM | disk | FTP | other *** search
Text File | 1997-12-11 | 12.5 KB | 451 lines | [TEXT/ALFA] |
- ## -*-Tcl-*-
- # ###################################################################
- # Vince's Additions - an extension package for Alpha
- #
- # FILE: "indentation.tcl"
- # created: 27/7/97 {1:08:08 am}
- # last update: 11/12/97 {7:51:32 pm}
- # Author: Vince Darley
- # E-mail: <darley@fas.harvard.edu>
- # mail: Division of Applied Sciences, Harvard University
- # Oxford Street, Cambridge MA 02138, USA
- # www: <http://www.fas.harvard.edu/~darley/>
- #
- # ###################################################################
- ##
-
- namespace eval indent {}
- namespace eval bind {}
- namespace eval text {}
-
- proc indentLine {} { bind::IndentLine }
-
- proc typeText {t} {
- if [isSelection] {
- deleteSelection
- }
- insertText $t
- }
-
- proc normalLeftBrace {} {
- typeText "\{"
- }
- proc normalRightBrace {} {
- typeText "\}"
- blink [matchIt "\}" [pos::math [getPos] - 2]]
- }
-
- proc literalChar {} {
- return [expr {[lookAt [pos::math [getPos] - 1]] == "\\"}]
- }
-
- # ◊◊◊◊ Electric indentation ◊◊◊◊ #
- proc bind::LeftBrace {} {
- if [isSelection] { deleteSelection }
- global elecLBrace mode
- if {![info exists elecLBrace] || !$elecLBrace} {
- insertText "\{"
- return
- }
- if {![catch {mode::proc electricLeft}]} {return}
- if {![catch {search -l [lineStart [pos::math [lineStart [getPos]] - 1]] \
- -s -f 0 -r 0 "\}" [getPos]} res]} {
- set end [getPos]
- if {[pos::compare [getPos] != [maxPos]]} {
- incr end
- }
-
- if {[regexp {\}[ \t\r\n]*else} [getText [lindex $res 0] $end]]} {
- set res2 [search -s -f 0 -r 1 {else} [getPos]]
- oneSpace
- set text [getText [lindex $res2 0] [getPos]]
- if {[lookAt [pos::math [getPos] - 1]] != " "} {
- append text " "
- }
- replaceText [pos::math [lindex $res 0] + 1] [getPos] " $text\{\r"
- bind::IndentLine
- return
- }
- }
- set pos [getPos]
- set i [text::firstNonWsLinePos $pos]
- if {([pos::compare $i == $pos]) || ([lookAt [pos::math $pos - 1]] == " ")} {
- insertText "\{\r" [text::indentString $pos] "\t"
- } else {
- insertText " \{\r" [text::indentString $pos] "\t"
- }
- }
-
- proc bind::RightBrace {} {
- if [isSelection] { deleteSelection }
- global elecRBrace mode
- if {![info exists elecRBrace] || !$elecRBrace} {
- insertText "\}"
- catch {blink [matchIt "\}" [pos::math [getPos] - 2]]}
- return
- }
- if {![catch {mode::proc electricRight}]} {return}
- set pos [getPos]
- set start [lineStart $pos]
-
- if {[catch {matchIt "\}" [pos::math $pos - 1]} matched]} {
- beep
- message "No matching '\{'!"
- return
- }
- set text [getText [lineStart $matched] $matched]
- regexp {^[ ]*} $text indentation
- if {[string trim [getText $start $pos]] != ""} {
- insertText "\r" $indentation "\}\r" $indentation
- blink $matched
- return
- }
- set text "${indentation}\}\r$indentation"
- replaceText $start $pos $text
- goto [pos::math $start + [string length $text]]
- blink [matchIt "\}" [pos::math $start - 2]]
- }
-
- proc bind::electricSemi {} {
- if [isSelection] { deleteSelection }
- global electricSemi mode
- if {![info exists electricSemi] || !$electricSemi} {
- insertText ";"
- return
- }
- if {![catch {mode::proc electricSemi}]} {return}
- set pos [getPos]
- set start [lineStart $pos]
- set text [getText $start $pos]
-
- if {[string first "for" $text] != "-1"} {
- set paren 0
- set len [string length $text]
- for {set i 0} {$i < $len} {incr i} {
- switch -- [string index $text $i] {
- "(" { incr paren }
- ")" { incr paren -1 }
- }
- }
- if {$paren != 0} {
- insertText ";"
- return
- }
- }
-
- insertText ";\r" [text::indentString $pos]
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "bind::CarriageReturn" --
- #
- # General purpose CR procedure. Should be bound to 'return' for all
- # modes really. Calls a mode-specific procedure if required.
- # -------------------------------------------------------------------------
- ##
- proc bind::CarriageReturn {} {
- if [isSelection] { deleteSelection }
- if [text::isInComment [set p [getPos]] start] {
- # special case for beginning of line
- if {[pos::compare $p == [lineStart $p]]} {
- backwardChar
- }
- insertText "\r${start}"
- return
- }
- global mode
- if [catch {mode::proc carriageReturn}] {
- insertText "\r"
- global indentOnCR
- if $indentOnCR {bind::IndentLine}
- }
- }
-
- proc bind::IndentLine {} {
- global mode
- if [catch {mode::proc indentLine}] {
- text::genericIndent
- }
- }
-
- proc insertActualTab {} { typeText "\t" }
-
- proc bind::_haveElectricColon {} {
- global mode
- global ${mode}modeVars
- if [info exists ${mode}modeVars(electricColon)] {
- return [set ${mode}modeVars(electricColon)]
- } else {
- return 0
- }
- }
-
-
- ##
- # -------------------------------------------------------------------------
- #
- # "text::isInComment" --
- #
- # Are we in a block comment? Just checks if both the given line and the
- # next line commence with any of a set of known block-comment characters.
- # Not 100% satisfactory for C comments, but fine for all others.
- # -------------------------------------------------------------------------
- ##
- proc text::isInComment {pos {st ""}} {
- set p [lineStart $pos]
- if {[pos::compare $pos == $p] && [pos::compare $p != 0]} {
- set pos [pos::math $pos - 1] ; set p [lineStart $pos]
- }
- set q [nextLineStart $pos]
- set t [getText $p $q]
- if { $st != "" } {
- upvar $st a
- }
- foreach commentCh [commentCharacters "General"] {
- if [regexp "^\[ \t\]*[quote::Regfind ${commentCh}]\[ \t\]*" $t a] {
- # if we hit return in the middle of a line
- if {[string trim [getText $pos $q]] != "" && [pos::compare $pos != $p]} {
- return 1
- }
- # if the next line is a comment
- if [catch {text::firstNonWsLinePos $q} qq] { return 0 }
- if {[getText $qq [pos::math $qq + [string length $commentCh]]] == $commentCh} {
- return 1
- }
- }
- }
- return 0
- }
-
-
- # ◊◊◊◊ Indentation utility routines ◊◊◊◊ #
-
- proc posX {pos} {return [lindex [posToRowCol $pos] 1] }
- # the above version doesn't work! Need to ask Pete to fix it.
- proc posX {pos} {return [string length [text::maxSpaceForm [getText [lineStart $pos] $pos]]]}
-
- proc text::firstNonWs {pos} {
- set p [text::firstNonWsPos $pos]
- if {[pos::compare $p > 0]} {
- return [lookAt $p]
- } else {
- return ""
- }
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "text::firstNonWsPos" --
- #
- # This returns the position of the first non-whitespace character from
- # the start of pos' line. It need not return something on the same
- # line.
- # -------------------------------------------------------------------------
- ##
- proc text::firstNonWsPos {pos} {
- return [lindex [search -s -f 1 -r 1 {[^ \t\r]} [lineStart $pos]] 0]
- }
-
- proc text::firstNonWsLinePos {pos} {
- return [lindex [search -s -f 1 -r 1 {[^ \t]} [lineStart $pos]] 0]
- }
-
- proc text::indentation {pos} {
- return [search -s -m 0 -f 1 -r 1 {^[ \t]*[^ \t]} [lineStart $pos]]
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "text::minSpaceForm" --
- #
- # Converts to minimal form: tabs then spaces. Uses one regsub to do
- # the job. Note that the regexp used relies upon the left-to-right
- # priority of branch matching. If the regexp library used is more
- # sophisticated and finds maximal matches, then this is no good.
- # In that case use:
- # regsub -all $sp $ws "\t" ws
- # regsub -all " +\t" $ws "\t" ws
- # -------------------------------------------------------------------------
- ##
- proc text::minSpaceForm {ws} {
- regsub -all "([spacesEqualTab]| +\t)" $ws "\t" ws
- return $ws
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "text::maxSpaceForm" --
- #
- # Converts it to maximal form - just spaces.
- # Just uses one funky regsub to do the job! Takes account of tab-size,
- # spaces interspersed with tabs,...
- # -------------------------------------------------------------------------
- ##
- proc text::maxSpaceForm {ws} {
- set sp [spacesEqualTab]
- regsub -all "(($sp)*) *\t" $ws "\\1$sp" ws
- return $ws
- }
-
- ##
- # -------------------------------------------------------------------------
- #
- # "spacesEqualTab" --
- #
- # Return the number of spaces equivalent to a single tab.
- # -------------------------------------------------------------------------
- ##
- proc spacesEqualTab {} {
- getWinInfo a
- string range " " 1 $a(tabsize)
- }
-
- proc doubleLookAt {pos} {return [getText $pos [pos::math $pos + 2]]}
-
- set bind::_IndentSpaces " "
- set bind::_IndentTabs "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"
-
- proc text::indentOf {size} {
- global bind::_IndentSpaces bind::_IndentTabs
- getWinInfo a
- set ret [string range ${bind::_IndentTabs} 1 [expr $size / $a(tabsize)]]
- append ret [string range ${bind::_IndentSpaces} 1 [expr $size % $a(tabsize)]]
- return $ret
- }
-
- # returns the indent string of the line named by 'pos'
- proc text::indentString {pos} {
- set beg [lineStart $pos]
- set text [getText $beg [nextLineStart $beg]]
- if [regexp {^[ \t]*} $text white] { return $white } else { return "" }
- }
-
- # returns the indent string of the line named by 'pos'
- proc text::indentTo {pos} {
- if [regexp {^[ \t]*} [getText [lineStart $pos] $pos] white] { return $white } else { return "" }
- }
-
- proc text::halfTab {} {
- global indent_amounts
- return [string range " " 1 $indent_amounts(1)]
- }
- proc text::Tab {} {
- global indentationAmount
- return [text::indentOf $indentationAmount]
- }
-
- proc text::getTabSize {} {
- getWinInfo a
- return $a(tabsize)
- }
-
- # ◊◊◊◊ General purpose indentation ◊◊◊◊ #
-
- proc indentRegion {} {
- global mode
- if {![catch {mode::proc indentRegion}]} {return}
- simpleIndentRegion
- }
-
- ##########################################################################
- # #
- # Stuff below here is largely unchanged from Pete's "electric.tcl". #
- # I've just put it here so I can totally override that file and make #
- # changes more easily myself. #
- # #
- ##########################################################################
-
- proc simpleIndentRegion {} {
- set from [lindex [posToRowCol [getPos]] 0]
- set to [lindex [posToRowCol [selEnd]] 0]
- select [getPos]
- while {$from <= $to} {
- goto [rowColToPos $from 0]
- bind::IndentLine
- incr from
- }
- }
-
- set PerlcommentRegexp {^[ \t]*#}
- set cCommentRegexp {/\*([^*]|[^*]\/|\*[^\/]|\r)*\*/}
-
- #########################################################################
- # Generic C-style indentation (works for Tcl and Perl)
- # Significant changes by Vince.
- proc text::genericIndent {} {
- global mode
- global ${mode}commentRegexp cCommentRegexp
- if {[info exists ${mode}commentRegexp]} {
- set comPat [set ${mode}commentRegexp]
- } else {
- set comPat $cCommentRegexp
- }
- set comPat "($comPat|^\[ \t\]\t*$)"
-
- # get details of current line
- set beg [lineStart [getPos]]
- set text [getText $beg [nextLineStart $beg]]
- regexp {^[ \t]*} $text white
- set len [string length $white]
- set epos [pos::math $beg + $len]
-
- # Find last previous non-comment line and get its leading whitespace
- set pos $beg
- set lst [search -s -f 0 -r 1 -i 0 -m 0 {^[ \t]*[^ \t\r]} [pos::math $pos - 1]]
- set line [getText [lindex $lst 0] [pos::math [nextLineStart [lindex $lst 0]] - 1]]
- set lwhite [posX [pos::math [lindex $lst 1] - 1]]
- # Find the last preceding comment block
- set prvPos [lindex $lst 0]
- if {![catch {search -s -f 0 -r 1 -i 0 $comPat [pos::math $pos - 1]} lstCmt]} {
- set begCmt [lindex $lstCmt 0]
- set endCmt [lindex $lstCmt 1]
- # If current non-blank line is in the comment...
- while {[pos::compare $begCmt <= $prvPos] && [pos::compare $endCmt >= $prvPos]} {
- # ...find the last non-blank line that precedes the comment block,
- if {![catch {search -s -f 0 -r 1 -i 0 {^[ \t]*[^ \t\r]} [pos::math $begCmt - 1]} lst]} {
- set prvPos [lindex $lst 0]
- set line [getText [lindex $lst 0] [pos::math [nextLineStart [lindex $lst 0]] - 1]]
- set lwhite [posX [pos::math [lindex $lst 1] - 1]]
- # ...and the next preceding comment block.
- if {![catch {search -s -f 0 -r 1 -i 0 $comPat [expr $prvPos]} lstCmt]} {
- set begCmt [lindex $lstCmt 0]
- set endCmt [lindex $lstCmt 1]
- } else {
- break
- }
- } else {
- # Handle search failure at top-of-file
- set line "#"
- set lwhite 0
- break
- }
- }
- }
-
- regexp {([^ \t])[ \t]*$} $line allofit nextC
- global indentationAmount
- if {($nextC == "\{")} {
- incr lwhite $indentationAmount
- } elseif {$nextC == ":" && [bind::_haveElectricColon]} {
- incr lwhite [expr $indentationAmount /2]
- }
-
- if {[regexp {:[ \t\r]*$} $text] && [bind::_haveElectricColon]} {incr lwhite [expr -$indentationAmount / 2]}
- if {[lookAt $epos] == "\}"} {
- incr lwhite [expr -$indentationAmount]
- }
-
- set lwhite [text::indentOf $lwhite]
- if {$white != $lwhite} {
- replaceText $beg $epos $lwhite
- }
- goto [pos::math $beg + [string length $lwhite]]
- }
-
-